﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using SpiceNet.Models;
using SpiceNet.Diagnostics;
using SpiceNet.Circuits;
using SpiceNet.Parameters;

namespace SpiceNet.Simulations
{
    /// <summary>
    /// Structures used to describe DC transfer curve analyses to be performed
    /// </summary>
    public class TrcvJob : Job
    {
        /// <summary>
        /// A class representing a sweeped variable
        /// </summary>
        public class TrcvSweep
        {
            // Sweep parameters
            public double TRCVvStart = 0.0; /* starting voltage/current */
            public double TRCVvStop = 0.0; /* ending voltage/current */
            public double TRCVvStep = 0.0; /* voltage/current step */
            public Parameter<double> TRCVvSave = null; /* voltage of this source BEFORE analysis to restore when done */

            public string TRCVvName = ""; /* source being varied */

            // public Type TRCVvType = 0; /* type of element being varied NOTE: We don't need it since C# can test for types  on TRCVvElt */
            public int TRCVset = 0; /* flag to indicate this nest level used */

            /// <summary>
            /// Constructor
            /// </summary>
            public TrcvSweep() { }

            /// <summary>
            /// Constructor
            /// </summary>
            /// <param name="vName">The name of the source to sweep</param>
            /// <param name="vStart">The starting value</param>
            /// <param name="vStop">The final value</param>
            /// <param name="vStep">The step</param>
            public TrcvSweep(string vName, double vStart, double vStop, double vStep)
            {
                TRCVvName = vName;
                TRCVvStart = vStart;
                TRCVvStop = vStop;
                TRCVvStep = vStep;
            }

            public Instance TRCVvElt = null;
        }

        /// <summary>
        /// Sweep variables
        /// </summary>
        public List<TrcvSweep> Sweeps { get; private set; } = new List<TrcvSweep>();

        /// <summary>
        /// Get the number of nest levels
        /// </summary>
        public int TRCVnestLevel => Sweeps.Count - 1;

        /// <summary>
        /// Iteration state during pause
        /// </summary>
        public int TRCVnestState { get; set; }

        /// <summary>
        /// Constructor
        /// </summary>
        public TrcvJob(string name) : base(name) { }

        /// <summary>
        /// Constructor
        /// Automatically adds one sweep to the DC job
        /// </summary>
        /// <param name="name">The name of the source to sweep</param>
        /// <param name="vStart">The starting value of the sweep</param>
        /// <param name="vStop">The stopping value of the sweep</param>
        /// <param name="vStep">The step of the value</param>
        public TrcvJob(string name, double vStart, double vStop, double vStep) : base(name)
        {
            Sweeps.Add(new TrcvSweep(name, vStart, vStop, vStep));
        }

        /// <summary>
        /// Get or set simulation parameters
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public override object this[string key]
        {
            get
            {
                if (!string.IsNullOrEmpty(key))
                {
                    Match m = Regex.Match(key, @"^(?<base>\w+)(?<level>\d+)$");
                    if (m.Success)
                    {
                        int level = int.Parse(m.Groups["level"].Value) - 1;

                        if (level > Sweeps.Count || level < 0)
                        {
                            CircuitWarning.Warning(this, "Parameter nesting level does not exist");
                            return null;
                        }
                        switch (m.Groups["base"].Value)
                        {
                            case "start": return Sweeps[level].TRCVvStart;
                            case "stop": return Sweeps[level].TRCVvStop;
                            case "step": return Sweeps[level].TRCVvStep;
                            case "name": return Sweeps[level].TRCVvName;
                        }
                    }
                }
                CircuitWarning.Warning(this, string.Format("Trcv {0}: Unrecognized parameter {1}", Name, key));
                return null;
            }
            set
            {
                if (!string.IsNullOrEmpty(key))
                {
                    Match m = Regex.Match(key, @"^(?<base>\w+)(?<level>\d+)$");
                    if (m.Success)
                    {
                        int level = int.Parse(m.Groups["level"].Value) - 1;
                        if (level == Sweeps.Count)
                            Sweeps.Add(new TrcvSweep());
                        else if (level < 0 || level > Sweeps.Count)
                        {
                            CircuitWarning.Warning(this, "Invalid level");
                            return;
                        }
                        switch (m.Groups["base"].Value)
                        {
                            case "start": Sweeps[level].TRCVvStart = Convert.ToDouble(value); break;
                            case "stop": Sweeps[level].TRCVvStop = Convert.ToDouble(value); break;
                            case "step": Sweeps[level].TRCVvStep = Convert.ToDouble(value); break;
                            case "name": Sweeps[level].TRCVvName = Convert.ToString(value); break;
                        }
                    }
                }
                CircuitWarning.Warning(this, string.Format("Trcv {0}: Unrecognized parameter {1}", Name, key));
            }
        }
    }
}
